#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File    :   reptile.py
@Time    :   2024/01/16 16:12:58
@Author  :   chakcy 
@Email   :   947105045@qq.com
@description   :   爬虫父类
'''

from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
import PySimpleGUI as sg

from concurrent.futures import ThreadPoolExecutor, as_completed
import queue
import threading

from pyquery import PyQuery as pq

import time
import sqlite3
from abc import ABC, abstractmethod
from reptile.wait_elemnet import wait_for_element_with_id

MAX_WORKERS = 5
js="window.open('{}','_blank');"
lock = threading.Lock()

class Reptile(ABC):
    # 初始化
    def __init__(self, url:str, dataPath:str=None, list_template:str=None, sql_table:str=None, driver_path:str=None):
        # 初始化 selenium 配置
        self.options = Options()
        self.service = Service(executable_path=driver_path)
        self.options.add_experimental_option("excludeSwitches", ['enable-automation', 'enable-logging'])
        self.options.add_experimental_option('useAutomationExtension', False)
        self.options.add_experimental_option("detach", True)
        self.options.add_argument('--disable-blink-features=AutomationControlled')
        self.options.add_argument('--headless')
        if driver_path is not None:
            self.web = Chrome(service=self.service, options=self.options)
        else:
            self.web = Chrome(options=self.options)
        self.web.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
            'source': 'Object.defineProperty(navigator, "webdriver", {get: () => undefined})'
        })
        self.current_window = self.web.current_window_handle
        self.data_queue = queue.Queue()
        # 初始化数据库
        if dataPath is not None and sql_table is not None:
            self.conn = sqlite3.connect(dataPath)
            self.cursor = self.conn.cursor()
            self.cursor.execute(sql_table)
        self.dataPath = dataPath
        # 初始化网址
        self.url = url
        if list_template is not None:
            self.list_template = list_template

        self.dataList = []

    # 模拟登录
    def login(self, username:dict, password:dict, loginBtn:str):
        print("登录")
        self.web.get(self.url)
        wait_for_element_with_id(self.web, username["selector"].replace("#",""))
        time.sleep(1)
        print("输入用户名")
        usernameElement = self.web.find_element(By.CSS_SELECTOR, username["selector"])
        usernameElement.clear()
        usernameElement.send_keys(username["value"])
        print("输入密码")
        passwordElement = self.web.find_element(By.CSS_SELECTOR, password["selector"])
        passwordElement.clear()
        passwordElement.send_keys(password["value"])
        print("点击登录")
        loginBtnElement = self.web.find_element(By.CSS_SELECTOR, loginBtn)
        loginBtnElement.click()
    
    # 获取网址
    def getUrl(self, key:str, pages:int):
        self.key = key
        self.message_url_list = []
        for page in range(1, pages + 1):
            url = self.list_template.format(key, page)
            print(url)
            self.web.execute_script(js.format(url))
            self.web.switch_to.window(self.web.window_handles[-1])
            element = pq(self.web.page_source)
            self.message_url_list += self.parseUrl(element)
            self.web.close()
            self.web.switch_to.window(self.current_window)

    # 浏览器任务
    def browser_task(self ,driver: Chrome, idx: int):
        try:
            lock.acquire()
            print(self.message_url_list[idx])
            driver.execute_script(js.format(self.message_url_list[idx]))
            driver.switch_to.window(driver.window_handles[-1]) 
            element = pq(driver.page_source)
            dataDict = self.analysis(element)
            print(f"第{idx+1}条数据：{dataDict}")
            itemList = tuple([i for i in dataDict.values()])
            print(itemList)
            if(len(itemList)==0):
                return False
            try:
                self.data_queue.put(itemList)
                return True
            except Exception:
                return False
        except Exception:
            return False
        
        finally:
            driver.close()
            driver.switch_to.window(self.current_window)
            lock.release()

    # 获取数据
    def run(self):
        executor = ThreadPoolExecutor(max_workers=MAX_WORKERS)
        ths = list()
        for i, url in enumerate(self.message_url_list):
            th = executor.submit(self.browser_task, self.web, idx=i)
            ths.append(th)
            sg.one_line_progress_meter('爬虫进度', i, len(self.message_url_list), '进度')

        for future in as_completed(ths):
            print(future.result())

        while not self.data_queue.empty():  
            self.dataList.append(self.data_queue.get())  

    # 保存数据
    def save(self, sql):
        self.cursor.executemany(sql, self.dataList)
        self.conn.commit()

    # 关闭爬虫
    def close(self):
        self.cursor.close()
        self.conn.close()
        self.web.close()

    # 解析出网址
    @abstractmethod
    def parseUrl(self, element:pq):
        pass

    # 解析数据
    @abstractmethod
    def analysis(self, element:pq):
        pass
